package top.codingfly.aliyun.util;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.HexUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.codingfly.aliyun.entity.PlayInfo;
import top.codingfly.audio.TSParser;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class AliyunDownloadUtils {
    private static Logger logger = LoggerFactory.getLogger(AliyunDownloadUtils.class);

    private final static ThreadPoolExecutor downloadExecutor = new ThreadPoolExecutor(6, 6,
            0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>());

    public static void downloadAuthVideo(String videoId, String playAuth, String downloadTmpPath, String fileSaveParentPath) throws InterruptedException {
        String rand = UUID.randomUUID().toString();
        String encryptRand = EncryptUtils.encryptRand(rand);
        String aliUrl = "https://vod.cn-shanghai.aliyuncs.com/?";
        PlayInfo playerInfo = AliyunUtils.getVodPlayerInfo(aliUrl, encryptRand, playAuth, videoId, "");
        if (null == playerInfo) {
            throw new RuntimeException("获取阿里云视频播放信息失败");
        }
        // 获取分辨率最大的视频
        List<PlayInfo.PlayItem> playInfoList = playerInfo.playObject.playInfo;
        PlayInfo.PlayItem maxVideo = playInfoList.stream().max(Comparator.comparingLong(PlayInfo.PlayItem::getWidth)).get();
        String playURL = maxVideo.getPlayURL();
        if (!playURL.contains("m3u8")) {
            throw new RuntimeException("playURL不是m3u8");
        }
        String fileName = playerInfo.videoBase.title;
        String decryptKey = EncryptUtils.decrypt(rand, maxVideo.getRand(), maxVideo.getPlaintext());
        String m3u8FileContent = HttpUtil.get(playURL);
        logger.info("获取视频:{},m3u8文件:{} 内容成功", fileName, playURL);
        String tsBaseUrl = playURL.substring(0, playURL.lastIndexOf("/") + 1);
        List<String> allTsList = getAllTsUrlList(tsBaseUrl, m3u8FileContent);
        if (allTsList==null || allTsList.size()==0) {
            return;
        }
        String downloadTsPath = downloadTmpPath + File.separator + videoId + File.separator;
        CountDownLatch latch = new CountDownLatch(allTsList.size());
        allTsList.forEach(tsUrl -> {
            downloadExecutor.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        URI uri = new URI(tsUrl);
                        String fileName = uri.getPath().substring(uri.getPath().lastIndexOf("/") + 1);
                        byte[] download = HttpRequest.get(tsUrl).execute().bodyBytes();
                        TSParser parser = new TSParser();
                        TSParser.TSStream tsStream = parser.fromByteArray(download);
                        parser.decrypt(tsStream, HexUtil.decodeHex(decryptKey));
                        tsStream.dumpToFile(new File(downloadTsPath, fileName));
                        Thread.sleep(1500);
                        latch.countDown();
                    } catch (Exception e) {
                        latch.countDown();
                        logger.info("ts文件下载失败:{}", tsUrl, e);
                    }
                }
            });
        });
        latch.await();
        mergeHsToMp4(downloadTsPath, fileSaveParentPath, fileName);
    }

    private static List<String> getAllTsUrlList(String tsBaseUrl, String m3u8FileContent) {
        List<String> allTsList = new ArrayList();
        StringTokenizer tokenizer = new StringTokenizer(m3u8FileContent, "\n");
        StringBuilder sb = new StringBuilder(512);
        while (tokenizer.hasMoreTokens()) {
            String token = tokenizer.nextToken();
            if (!token.startsWith("#EXT-X-KEY"))
                sb.append(token).append("\n");
            if (token.startsWith("#EXTINF")) {
                if (tokenizer.hasMoreTokens()) {
                    String nextToken = tokenizer.nextToken();
                    allTsList.add(tsBaseUrl + nextToken);
                    sb.append(nextToken).append("\n");
                }
            }
        }
        return allTsList;
    }

    public static void mergeHsToMp4(String downloadTsPath, String fileSaveParentPath, String filename) {
        String fileOutPath = fileSaveParentPath + File.separator + filename + ".mp4";
        new File(fileSaveParentPath).mkdir();
        File tsParentFolder = new File(downloadTsPath);
        try (FileOutputStream fileOutputStream = new FileOutputStream(fileOutPath)) {
            byte[] bytes = new byte[1024];
            int length;
            for (File file : tsParentFolder.listFiles()) {
                try (FileInputStream fis = new FileInputStream(file);) {
                    while ((length = fis.read(bytes)) != -1) {
                        fileOutputStream.write(bytes, 0, length);
                    }
                    fileOutputStream.flush();
                } catch (IOException e) {
                    logger.error(e.getMessage(), e);
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            e.printStackTrace();
        }
        FileUtil.del(tsParentFolder);
    }

}
